Coverage Report

Created: 2026-02-05 09:02

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
D:\a\scloud-dns\scloud-dns\src\dns\packet\additional\mod.rs
Line
Count
Source
1
use crate::dns::q_class::DNSClass;
2
use crate::dns::q_name::parse_qname;
3
use crate::dns::q_type::DNSRecordType;
4
use crate::exceptions::SCloudException;
5
6
#[derive(PartialEq, Debug)]
7
pub(crate) struct AdditionalSection {
8
    pub(crate) q_name: String,
9
    pub(crate) q_type: DNSRecordType,
10
    pub(crate) q_class: DNSClass,
11
    pub(crate) ttl: u32,
12
    pub rdlength: u16,
13
    pub rdata: Vec<u8>,
14
}
15
16
impl AdditionalSection {
17
    /// Deserialize one AdditionalSection and return (section, consumed_bytes)
18
    ///
19
    /// # Exemple :
20
    /// ```
21
    /// use crate::dns::packet::additional::AdditionalSection;
22
    /// use crate::dns::q_type::DNSRecordType;
23
    /// use crate::dns::q_class::DNSClass;
24
    ///
25
    /// // Record A in additional section (ns1.example.com → 192.0.2.1)
26
    /// let raw_additional: Vec<u8> = vec![
27
    ///     0x03, b'n', b's', b'1',
28
    ///     0x07, b'e', b'x', b'a', b'm', b'p', b'l', b'e',
29
    ///     0x03, b'c', b'o', b'm',
30
    ///     0x00,             // End of QNAME
31
    ///     0x00, 0x01,       // TYPE = A
32
    ///     0x00, 0x01,       // CLASS = IN
33
    ///     0x00, 0x00, 0x01, 0x2c, // TTL = 300
34
    ///     0x00, 0x04,       // RDLENGTH = 4
35
    ///     192, 0, 2, 1,     // RDATA
36
    /// ];
37
    ///
38
    /// let (additional, consumed) =
39
    ///     AdditionalSection::from_bytes(&raw_additional, 0).unwrap();
40
    ///
41
    /// assert_eq!(additional.q_name, "ns1.example.com");
42
    /// assert_eq!(additional.q_type, DNSRecordType::A);
43
    /// assert_eq!(additional.q_class, DNSClass::IN);
44
    /// assert_eq!(additional.ttl, 300);
45
    /// assert_eq!(additional.rdata, vec![192, 0, 2, 1]);
46
    /// assert_eq!(consumed, raw_additional.len());
47
    /// ```
48
1
    pub(crate) fn from_bytes(
49
1
        buf: &[u8],
50
1
        offset: usize,
51
1
    ) -> Result<(AdditionalSection, usize), SCloudException> {
52
1
        let (q_name, consumed_name) = parse_qname(buf, offset).unwrap();
53
1
        let mut pos = consumed_name;
54
55
1
        if buf.len() < pos + 10 {
56
0
            return Err(SCloudException::SCLOUD_ADDITIONAL_DESERIALIZATION_FAILED_BUF_TOO_SHORT);
57
1
        }
58
59
1
        let q_type = DNSRecordType::try_from(u16::from_be_bytes([buf[pos], buf[pos + 1]])).unwrap();
60
1
        pos += 2;
61
62
1
        let q_class = DNSClass::try_from(u16::from_be_bytes([buf[pos], buf[pos + 1]])).unwrap();
63
1
        pos += 2;
64
65
1
        let ttl = u32::from_be_bytes([buf[pos], buf[pos + 1], buf[pos + 2], buf[pos + 3]]);
66
1
        pos += 4;
67
68
1
        let rdlength = u16::from_be_bytes([buf[pos], buf[pos + 1]]);
69
1
        pos += 2;
70
71
1
        if buf.len() < pos + rdlength as usize {
72
0
            return Err(
73
0
                SCloudException::SCLOUD_ADDITIONAL_DESERIALIZATION_FAILED_RDATA_OUT_OF_BOUNDS,
74
0
            );
75
1
        }
76
77
1
        let rdata = buf[pos..pos + rdlength as usize].to_vec();
78
1
        pos += rdlength as usize;
79
80
1
        Ok((
81
1
            AdditionalSection {
82
1
                q_name,
83
1
                q_type,
84
1
                q_class,
85
1
                ttl,
86
1
                rdlength,
87
1
                rdata,
88
1
            },
89
1
            pos,
90
1
        ))
91
1
    }
92
93
    /// Serialize the AdditionalSection into bytes
94
    ///
95
    /// # Exemple :
96
    /// ```
97
    /// use crate::dns::packet::additional::AdditionalSection;
98
    /// use crate::dns::q_type::DNSRecordType;
99
    /// use crate::dns::q_class::DNSClass;
100
    ///
101
    /// let additional = AdditionalSection {
102
    ///     q_name: "ns1.example.com".to_string(),
103
    ///     q_type: DNSRecordType::A,
104
    ///     q_class: DNSClass::IN,
105
    ///     ttl: 300,
106
    ///     rdlength: 4,
107
    ///     rdata: vec![192, 0, 2, 1],
108
    /// };
109
    ///
110
    /// let bytes = additional.to_bytes().unwrap();
111
    ///
112
    /// // NAME + TYPE + CLASS + TTL + RDLENGTH + RDATA
113
    /// assert!(bytes.len() > 20);
114
    /// ```
115
1
    pub(crate) fn to_bytes(&self) -> Result<Vec<u8>, SCloudException> {
116
1
        let mut buf: Vec<u8> = Vec::new();
117
118
3
        for label in 
self.q_name.split('.')1
{
119
3
            let len = label.len();
120
3
            if len > 63 {
121
0
                return Err(
122
0
                    SCloudException::SCLOUD_ADDITIONAL_DESERIALIZATION_FAILED_QNAME_TOO_LONG,
123
0
                );
124
3
            }
125
3
            buf.push(len as u8);
126
3
            buf.extend_from_slice(label.as_bytes());
127
        }
128
1
        buf.push(0x00);
129
130
1
        let qtype_u16 =
131
1
            u16::try_from(self.q_type).expect("Cannot convert AdditionalSection q_type to u16");
132
1
        buf.extend_from_slice(&qtype_u16.to_be_bytes());
133
134
1
        let qclass_u16 = u16::try_from(self.q_class).unwrap();
135
1
        buf.extend_from_slice(&qclass_u16.to_be_bytes());
136
137
1
        buf.extend_from_slice(&self.ttl.to_be_bytes());
138
1
        buf.extend_from_slice(&self.rdlength.to_be_bytes());
139
1
        buf.extend_from_slice(&self.rdata);
140
141
1
        Ok(buf)
142
1
    }
143
}